package org.sxp.common.aspect;


import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.sxp.common.annotation.DataFilter;
import org.sxp.common.constants.DataFilterConstants;
import org.sxp.common.dto.DataAuthorityDTO;
import org.sxp.common.dto.DataFilterDto;
import org.sxp.common.web.XpContextHolder;
import org.sxp.modules.sys.service.SysDeptService;
import org.sxp.modules.sys.service.SysRoleDeptService;
import org.sxp.modules.sys.service.SysUserRoleService;
import org.sxp.modules.sys.shiro.ShiroUtils;

import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 数据过滤，切面处理类
 * @Author Administrator
 * @Date 2021/1/21
 */
@Slf4j
public class DataFilterAspect {
    /**
     * 医保区划列
     */
    public static final String USER_ID_COLUMN = "user_id";
    /**
     * 机构字段列
     */
    public static final String DEPT_ID_COLUMN = "dept_id";
    /**
     * 自定义sql条件统一key名
     */
    public static final String DATA_FILTER_KEY = "sql_filter";

    private static ThreadLocal<DataFilterDto> dataFilterDtoLocal = new ThreadLocal<>();


    @Autowired
    private SysDeptService sysDeptService;
    @Autowired
    private SysUserRoleService sysUserRoleService;
    @Autowired
    private SysRoleDeptService sysRoleDeptService;

    /**
     * 统筹区编码长度
     * @Author 沈兴平
     * @Date 2020/11/4
     */
    private static final int POOL_AREA_CODG_LENGTH = 6;

    public Object dataFilterAround(ProceedingJoinPoint point) throws Throwable {
        boolean isFirst = false;
        DataFilterDto dataFilterDto = getCurrentDataFilter();
        if(dataFilterDto.getColumnSql().isEmpty()){
            isFirst = true;
        }
        MethodSignature signature = (MethodSignature) point.getSignature();
        DataFilter dataFilter = signature.getMethod().getAnnotation(DataFilter.class);
        DataAuthorityDTO authorityDTO = null;
        if(dataFilter != null && dataFilter.enable()){
            // 如果有注解优先取注解参数
            Method method = ((MethodSignature) point.getSignature()).getMethod();
            Object[] params = point.getArgs();
            // 取springEL表达式解析，拿到权限参数对象
            authorityDTO = StrUtil.isNotBlank(dataFilter.value()) ? parseExpression(dataFilter.value(), method, params) : null;
            buildSqlFilter(authorityDTO, dataFilter.column(), dataFilter.tableAlias());
        }else if(dataFilter == null){
            buildSqlFilter(authorityDTO, null, null);
        }
        Object result = null;
        try{
            result = point.proceed();
        }catch (Exception e){
            throw e;
        }finally {
            if(isFirst && (dataFilter == null || dataFilter.enable())){
                remove();
            }
        }
        return result;
    }

    public DataFilterDto buildSqlFilter(DataAuthorityDTO dataAuthorityDTO, String column, String tableAlias){
        DataFilterDto dataFilterDto = getCurrentDataFilter();
        try{
            if(ShiroUtils.isAdmin()){
                return dataFilterDto;
            }
        }catch (Exception ex){

        }
        if(dataFilterDto.getColumnSql().isEmpty()) {
            if (dataAuthorityDTO == null) {
                // 没有传的情况下取线程上下文-数据权限对象
                dataAuthorityDTO = (DataAuthorityDTO) XpContextHolder.getContext()
                        .getProperty(DataFilterConstants.DATA_AUTHORITY_KEY);
                log.debug("Service数据权限切面，没有手动传取控制类切面放入线程上下文的权限对象[{}]", JSON.toJSONString(dataAuthorityDTO));
            }
            if (dataAuthorityDTO != null) {
                // 部门权限
                if (StrUtil.isNotBlank(dataAuthorityDTO.getDeptId())) {
                    Set<String> deptIdList = new HashSet<>();
                    // 取用户角色对应的部门ID列表
                    List<Long> roleIdList = sysUserRoleService.queryRoleIdList(dataAuthorityDTO.getUserId());
                    if(roleIdList.size() > 0){
                        List<String> userDeptIdList = sysRoleDeptService.queryDeptIdList(roleIdList.toArray(new Long[roleIdList.size()]));
                        deptIdList.addAll(userDeptIdList);
                    }
                    // 取用户当前部门ID和子部门ID
                    deptIdList.add(dataAuthorityDTO.getDeptId());
                    List<String> subDeptList = sysDeptService.getSubDeptIdList(dataAuthorityDTO.getDeptId());
                    deptIdList.addAll(subDeptList);
                    // 角色数据权限对应的部门ID列表

                    // 生成SQL过滤条件
                    if(deptIdList.size() > 0){
                        String columnKey = StrUtil.isNotBlank(column) ? column : DEPT_ID_COLUMN;
                        String sqlStr = getDeptSqlFilter(deptIdList, columnKey, tableAlias);
                        if(StrUtil.isNotBlank(sqlStr)){
                            dataFilterDto.getColumnSql().put(columnKey, sqlStr);
                        }
                    }
                }

                // 用户权限
                if (dataAuthorityDTO.getUserId() != null) {
                    // 生成SQL过滤条件
                    String columnKey = StrUtil.isNotBlank(column) ? column : USER_ID_COLUMN;
                    String sqlStr = getUserSqlFilter(dataAuthorityDTO.getUserId().toString(), columnKey, tableAlias);
                    if(StrUtil.isNotBlank(sqlStr)){
                        dataFilterDto.getColumnSql().put(columnKey, sqlStr);
                    }
                }
            }else{
                log.info("上下文没有获取到用户数据权限对象!!!");
            }
        }
        return dataFilterDto;
    }

    /**
     * 删除末尾字符串
     * @Author Administrator
     * @Date 2021/1/21
     * @Param inStr
     * @Param suffix
     * @return java.lang.String
     **/
    public static String trimEnd(String inStr, String suffix) {
        if (inStr.endsWith(suffix)) {
            return (inStr.substring(0,inStr.length()-suffix.length()));
        }
        return inStr;
    }


    /**
     * 获取用户数据过滤的SQL
     */
    private static String getUserSqlFilter(String value,String column, String tableAlias){
        //获取表的别名
        if(StrUtil.isNotBlank(tableAlias)){
            tableAlias +=  ".";
        }else if(tableAlias == null){
            tableAlias = "";
        }

        StringBuilder sqlFilter = new StringBuilder();
        //此处拼接逻辑判断
        if(StrUtil.isNotBlank(value)){
            sqlFilter.append(tableAlias)
                    .append(column)
                    .append(" = '")
                    .append(value)
                    .append("'");
        }

        if("".equals(sqlFilter.toString().trim())){
            return null;
        }

        return sqlFilter.toString();
    }

    /**
     * 获取部门数据过滤的SQL
     */
    private static String getDeptSqlFilter(Set<String> value, String column, String tableAlias){
        //获取表的别名
        if(StrUtil.isNotBlank(tableAlias)){
            tableAlias +=  ".";
        }else if(tableAlias == null){
            tableAlias = "";
        }

        StringBuilder sqlFilter = new StringBuilder();
        //此处拼接逻辑判断
        if(ArrayUtil.isNotEmpty(value)){
            sqlFilter.append(tableAlias).append(column).append(" in('").append(String.join("','", value)).append("')");
        }

        if("".equals(sqlFilter.toString().trim())){
            return null;
        }

        return sqlFilter.toString();
    }

    private DataAuthorityDTO parseExpression(String expression, Method method, Object [] args){

        //获取被拦截方法参数名列表(使用Spring支持类库)
        LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
        String[] paraNameArr = u.getParameterNames(method);

        //使用SPEL进行key的解析
        ExpressionParser parser = new SpelExpressionParser();
        //SPEL上下文
        StandardEvaluationContext context = new StandardEvaluationContext();
        //把方法参数放入SPEL上下文中
        for(int i=0;i<paraNameArr.length;i++){
            context.setVariable(paraNameArr[i], args[i]);
        }
        return parser.parseExpression(expression).getValue(context, DataAuthorityDTO.class);
    }

    public static DataFilterDto getCurrentDataFilter(){
        // 取上下文数据过滤对象
        DataFilterDto dataFilter = dataFilterDtoLocal.get();
        if(dataFilter == null){
            dataFilter = new DataFilterDto();
            dataFilterDtoLocal.set(dataFilter);
        }
        return dataFilter;
    }

    public static void remove(){
        dataFilterDtoLocal.remove();
    }
}
